package shooter.network;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import mo.network.client.listeners.IReceiver;
import mo.network.client.listeners.ISender;
import mo.network.server.NetworkServer;
import mo.network.server.data.Packet;
import shooter.Hauptfenster;
import shooter.Shooter;

/**
 * Represents a server for this game.
 * 
 * @author Michail Tausch
 */
public class crossServer implements IReceiver, ISender {

	NetworkServer server;
	Hauptfenster hauptFenster;

	Packet currentStatePacket = null;

	Object lock = new Object();
	private StateMessage currentMsg;
	
	Shooter shooter = null;
	
	boolean gameStarted = false;
	
	/**
	 * The constructor. It Initializes the server.
	 * @param hauptFenster
	 */
	public crossServer(Hauptfenster hauptFenster) {
		this.hauptFenster = hauptFenster;

		this.server = new NetworkServer();
		NetworkServer.setMaxDownload(10 * 1024 * 1024);
		NetworkServer.setMaxUpload(10 * 1024 * 1024);
		try {
			server.initServer();
			/*
			 * Nur die suche, um localhost auf die Netzwerkkarte zu mappen.
			 */
			NetworkInterface[] interfaces = server.getInterfaces();
			InetAddress localHost = Inet4Address.getLocalHost();
			hauptFenster.ip=(InetAddress) Inet4Address.getLocalHost();
			NetworkInterface toUse = null;
			for (NetworkInterface curr : interfaces) {
				Enumeration<InetAddress> addrs = curr.getInetAddresses();
				while (addrs.hasMoreElements()) {
					InetAddress currAddr = addrs.nextElement();
					if (currAddr.equals(localHost)) {
						toUse = curr;
						break;
					}
				}
			}
			// Weiter mit der Netzwerkkarte in toUse
			server.selectInterface(toUse, NetworkServer.ADDR_TYPE_INET4);
			server.setServerPort(15000);
			server.addReceiveListener(this);
			server.addSendListener(this);
			new Thread(server).start();
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Handles all packages the server receives.
	 */
	@Override
	public void packetReceived(Packet thePacket) {
		try {
			Message message = Message.readMessage(thePacket.getPayLoad());
			if (message instanceof connectMessage) {
				if (gameStarted){
					connectMessage cMessage = new connectMessage(connectMessage.gameStarted);
					server.queueUpload(new Packet(cMessage.toByteArray(), thePacket.getSocket()));
					return;
				}
				if (!hauptFenster
						.playersChanged(((connectMessage) message).name)) {
					connectMessage cMessage =
							new connectMessage(connectMessage.deny);
					server.queueUpload(new Packet(cMessage.toByteArray(),
							thePacket.getSocket()));
				}
			}
			if (message instanceof MovementMessage){
				shooter.setMovementState((MovementMessage)message);
			}
			if (message instanceof ChatMessage){
				this.sendMessage((ChatMessage)message);
				hauptFenster.addMessageToChatTextPane((ChatMessage)message);				
			}
			if (message instanceof QuitMessage){
				server.disconnectClient(thePacket.getSocket());
				hauptFenster.removePlayer((QuitMessage)message);
			}
//			if (message instanceof PingMessage){
//				this.shooter.setPlayerPing((PingMessage)message);
//			}
		} catch (Exception e) {
		}
	}
	
	/**
	 * Stops the server.
	 */
	public void kill() {
		server.killServer();
	}
	
	/**
	 * Sends a message to all clients.
	 * @param message
	 */
	public void sendMessage(Message message) {
		try {
			if (message instanceof StateMessage) {
				synchronized (lock) {
					if (this.currentMsg == null) {
						this.currentMsg = (StateMessage) message;
						this.currentStatePacket =
								new Packet(message.toByteArray(), null);
						server.queueForAll(this.currentStatePacket);
					} else {
						this.currentMsg = (StateMessage) message;
					}
				}
			} else {
				server.queueForAll(new Packet(message.toByteArray(), null));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Prints a message if a packet is lost or failed.
	 */
	@Override
	public void packetFailed(Packet thePacket) {
		System.err.println("Packet failed o_O");
	}

	/**
	 * Sends a message to all clients.
	 */
	@Override
	public void packetSent(Packet thePacket) {
		synchronized (lock) {
			if (this.currentMsg != null) {
				try {
					this.currentStatePacket =
							new Packet(this.currentMsg.toByteArray(), null);
					this.server.queueForAll(this.currentStatePacket);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * Sets the shooter of this server.
	 * @param shooter
	 */
	public void setShooter(Shooter shooter) {
		this.shooter = shooter;
	}

	/**
	 * Is the game running?
	 * @return
	 */
	public boolean isGameStarted() {
		return gameStarted;
	}
	
	/**
	 * Sets the game started or not.
	 * @param gameStarted
	 */
	public void setGameStarted(boolean gameStarted) {
		this.gameStarted = gameStarted;
	}	
}
